Developers of many application programs (“applications”) implement the applications so that they can be customized by third parties. To customize an application, a third party develops custom code (e.g., add-ins and document-level customizations) that uses functionality exposed by the application. The custom code may improve the usability of the applications or provide additional functionality (e.g., domain-specific functionality). Such applications are referred to as “host applications” because the custom code is hosted within the process of the application. Developers of applications typically want to encourage the development of custom code for their applications to increase the demand for their applications. As a result, such developers may provide “custom code runtimes” that facilitate the development of custom code. A runtime is code that is loaded along with custom code and provides services to the custom code. These services may include higher-level functionality than that exposed by the application or may include domain-specific functionality. When an application is to load and start the execution of custom code, the application may load the runtime and direct the runtime to load and start the execution of the custom code.
Because of the ease of developing custom code as “managed code,” many applications support the execution of custom code in the .NET Framework provided by Microsoft Corporation. The .NET Framework provides a common language runtime (“CLR”) that provides high-level operating system type services to the managed programs (including custom code and applications) and serves as an execution engine for managed programs. The CLR ensures that managed programs do not take any unauthorized action. As such, the CLR acts as a “sandbox” within which managed programs execute. The CLR provides application domains (“appdomains”) in which different managed programs can execute to help ensure that an errant managed program will not unduly affect the execution of another managed program.
In some environments, both host applications and custom code execute as managed code. A developer of a host application that executes as managed code defines objects (e.g., adhering to the Component Object Model of Microsoft Corporation) that are exposed to the custom code. Because the developer of a host application and the developers of custom code for the host application typically have different product release cycles, the current versions of their products may not be compatible when the custom code is statically bound (e.g., at compile time) to an exposed object. In such a case, when the developer changes the type of an exposed object in a new version of the host application, the current version of the custom code, which was developed based on the old type, may be incompatible with the new type. To address this incompatibility, a Managed Add-in Framework (“MAF”) has been developed that allows custom code to dynamically bind (e.g., at runtime) to exposed objects of a host application that executes as managed code. An embodiment of MAF is described in U.S. application Ser. No. 11/167,728, entitled “Managed Automation Programming Model” and filed on Jun. 27, 2005, now U.S. Pat. No. 7,523,444 issued Apr. 21, 2009.
FIG. 1 is a block diagram that illustrates remote access of a server object by client code. A server object is any object exposed by an application domain for access by client code executing in a different application domain. For example, a host application may expose a server object for use by custom code that executes in a different application domain. A server application domain 110 includes a server object 111 and a type object 112. The server object is exposed so that it can be accessed by client code executing in a client application domain. To enable remote access across application domains, the server class inherits the object class and the marshal by reference object class. The marshal by reference object class provides the functionality that supports remote access in the .NET Framework. A client application domain 120 includes a real proxy object 121, a transparent proxy object 122, client code 123, and a type object 124. The real proxy object is provided by the remote access functionality, and the transparent proxy object is provided by the real proxy object. The transparent proxy object is an object that looks to the client code as if it were an actual instance of the server object. Thus, the client code can invoke methods of the transparent proxy object just as it would invoke methods of an actual instance of the server object. When the transparent proxy object receives an invocation of a method by the client code, it forwards to the real proxy object an invocation request that includes the identifier of the server object, an identifier of the method to be invoked, and any actual parameters. The real proxy object forwards the invocation request to the identified server object. Upon receiving the invocation request, the marshal by reference object functionality of the server object invokes the method passing the actual parameters. When the method completes, any returned parameters are passed to the real proxy object, which provides them to the transparent proxy object for returning to the client code.
This remote access of objects works well in static binding situations but has some difficulties in dynamic binding situations. The object class provides general functionality to all objects including support for dynamic binding via “reflection.” To support dynamic binding, the object class provides a get type method that provides a reference to a type object that provides type information (e.g., method names and formal parameters) for the type of an object that inherits the object class. Once code has a reference to a type object for the class of an object, the code can invoke methods of the type object to retrieve the type information for that object. When the code wants to invoke a method of the object dynamically, it can invoke an invoke method of the type object passing a reference to the object, an identifier of a method, and any actual parameters. The invoke method of the type object then calls the corresponding method of the object passing the parameters and then returns any out parameters.
When client code executes in a separate application domain from a server object, the client code needs to instantiate in its application domain a type object for the class of the server object to enable dynamic binding. If the client application domain has access to the type information of the server class, then the client code can dynamically bind to the server object. For various reasons, however, it may not be desirable to allow client code to have access to such type information. Access to the type information of a server object may confer access to other server code, which may not be desired. Also, it can be administratively difficult to ensure that all client code has proper access to all the type information of all the server objects it needs to access.
If client code does not have access to type information of the server object, then the transparent proxy object will instantiate a type object for the closest parent class for which type information is available. For example, when the type information for the server object is not available, the transparent proxy object will instantiate a type object for the marshal by reference object class. When the client object tries to dynamically bind to the server object, an error will occur because the type object has no type information for the server object.
FIG. 2 is a block diagram that illustrates a solution that overcomes the problem of not having access to the type information of a server object. A server application domain 210 includes a server object 211, a type object 212, a remote object adapter object 213, and a remote type adapter object 214. A client application domain 220 includes a real proxy object 221, a transparent proxy object 222, client code 223, a remote type object 224, a real proxy object 225, a transparent proxy object 226, and a remote object 227. The server object 211 and type object 212 function in the same way as server object 111 and type object 112 (FIG. 1), except that the server object does not need to inherit the marshal by reference object class. The remote object adapter object exposes an IRemoteObjectContract interface that allows the client code to invoke the methods of the server object. When the remote object adapter object receives an invocation request for the server object, it invokes the corresponding method of the server object and returns any out parameters to the client code via the remote object. When the client code invokes the get type method of the remote object, the remote object sends an invocation request to the remote object adapter object. The remote object adapter object invokes the get type method of the server object, which causes the type object for the server class to be instantiated. Upon receiving a reference to the type object, the remote object adapter object instantiates the remote type adapter object and provides the reference to the type object. The remote type adapter object inherits the object class and the marshal by reference object class so that the client application domain can access the remote type adapter object remotely. The remote type adapter object exposes an IRemoteTypeContract interface through which the remote type object of the client application domain can access the type information of the type object for the server class. The remote type object of the client application domain inherits the type class so that it provides a type interface to the client code. The remote object adapter object returns a reference to the IRemoteTypeContract interface to the remote object, which instantiates the remote type object providing a reference to the remote type adapter object. The remote type object sets up a communications link to the remote type adapter object. The remote object then returns a reference to the remote type object to the client code.
To dynamically bind to the server object, the client code invokes get type information methods of the remote type object. The remote type object forwards an invocation request to the remote type adapter object, which in turn invokes the corresponding method of the type object for the server class. The out parameters are forwarded from the remote type adapter object to the remote type object, which returns the parameters to the client code. When the client code invokes the invoke method of the remote type object, the invocation request is sent to the remote type adapter object, which invokes the invoke method of the type object of the server class. The type object then invokes the corresponding method of the server object and returns any out parameters to the remote type adapter object, which forwards them to the remote type object for returning to the client code. Thus, the client code can access the type information for the server object even though it does not have direct access to the type information within its application domain.
A problem with the use of a remote object that inherits the object class is that the get type method of the object class is not a virtual method. As such, the remote object cannot override the get type method of the object class. So, in order for the client object to invoke the get type method of the remote object, it needs to be developed to specifically invoke the get type method of the remote object. As such, existing client code that invokes the get type method of the object class will not work correctly in this environment.